package com.example.calculation;



import com.example.entity.CalRecipe;
import com.example.entity.Chef;
import com.example.entity.Effect;
import com.example.entity.Equip;
import com.example.entity.OfficialGameData;
import com.example.entity.Skill;

import java.util.List;

/**
 * 缓存计算过程中可以复用的一些数据
 *
 * @author 胡帅博
 * @date 2020/9/28 16:54
 *
 *
 * 对于一个多为数组 new int[a][b][c]  最好可以让c>=a,c>=b 这样占用的内存空间最少,因为需要创建a*b个数组
 *
 */
public class TempCalCache{

    //厨师数量
     int chefCount;
    //菜谱数量
    int recipeCount;
    //设备数量
    int equipCount;

    /**
     * 得分计算公尺为 Math.ceil(price * (1 +reward + q + s));
     * price 价格
     * reward 厨神奖励倍数
     * q技法加成
     * s技能加成
     * <p>
     * 因为q和s是动态变化的，所以这里只存 price*(1+reward)
     * <p>
     * 如果使用scoreCache，则计算公尺变为  scoreCache[chefid][equipid][recipeid]+price*(q+s),少了一次加法运算，多了一次三维数组的寻址操作()
     * <p>
     *
     * [厨师id][厨具id][CalRecipe.index]  ,单份菜谱得分
     *
     */
    int[][][] scoreCache = null;


    /**
     * [厨师id][CalRecipe.index]  ,单份菜谱得分
     * */
    int[][] socreCacheNoEquip = null;

    /**
     *scoreGroupCache中存的是更进一步的缓存  一个厨师要做三个菜，CalRecipeArray.index指向的就为 三个菜谱搭配数量的组合，生成的索引
     *
     * 前置工作保证数组到index的映射是一对一的关系
     *
     * [厨师id][厨具id][CalRecipeArray.index]  ,单份菜谱得分
     *
     * CalRecipesindex代表的是一个有三个菜谱，每个菜谱的数量综合生成的id
     *
     */
    int[][][] scoreGroupCache = null;


    //技法加成的所有可选值 -1代表不能做 0代表能做但是没加成 0.1代表加成是10%，一次类推
    private double[] qualityAdd = {-1f, 0f, 0.1f, 0.3f, 0.5f,1.0f};


    /**
     * 售价对菜谱的加成（百分比）
     * <p>
     * qualityAddS中保存的是厨师技能搭配厨具技能,对各个菜谱的售价加成
     * 需要注意只有当 一个菜是可以做的时候，qualityAddS中的值才有意义
     * <p>
     * 三个维度分别是
     * [厨师id][厨具id]【菜谱id】 =技能对技能的加成
     */
    double[][][] qualityAddS = null;

    /**
     * 品质对菜谱的加成（百分比）
     * <p>
     * 厨神大赛计算中，上场三个厨师，有一些厨师的技法比较特殊，能给队友提供技法加成。但是这里没有考虑，
     * qualityAddQ中保存的是无特殊加成情况下，厨师技法搭配厨具技法加成,对各个菜谱的技法加成
     * 因此对于特殊技法，需要单独计算
     *
     * <p>
     * 三个维度分别是
     * [厨师id][厨具id]【菜谱id】 =技法对技能的加成,
     */
    double[][][] qualityAddQ = null;

    // 如果使用默认构造函数，这个类需要200+M的空间
    public TempCalCache() {

    }


    /**
     * @param chefIndexMax   厨师id的最大值
     * @param recipeIndexMax 菜谱id的最大值,一般整个计算过程中实际会用的菜谱数量不会超过100，可以将菜谱id做亚索
     * @param equipIndexMax  设备id的最大值
     */
    public TempCalCache(int chefIndexMax, int equipIndexMax, int recipeIndexMax) {
        this.chefCount = chefIndexMax;
        this.recipeCount = recipeIndexMax;
        this.equipCount = equipIndexMax;
        //菜谱数量 不可能超过50的，所以这里取五十个
        scoreCache = new int[chefIndexMax][equipIndexMax][recipeIndexMax];
        qualityAddS = new double[chefIndexMax][equipIndexMax][recipeIndexMax];
        qualityAddQ = new double[chefIndexMax][equipIndexMax][recipeIndexMax];
        socreCacheNoEquip=new int[chefIndexMax][recipeIndexMax];
    }

    public static class builder{

        TempCalCache tempCalCache;
        double[] reward = null;
        OfficialGameData officialGameData = null;
        GlobalAddtion globalAddtion = null;
        CacheKitchenGodCal kitchenGodCal = null;
        Chef[] ownChefs = null;
        CalRecipe[] ownRecipes = null;
        Equip[] ownEquips = null;


        public void init(Chef[] ownChefs, CalRecipe[] ownRecipes, Equip[] ownEquips, OfficialGameData officialGameData, GlobalAddtion globalAddtion, double[] reward) {
            this.officialGameData = officialGameData;
            this.globalAddtion = globalAddtion;
            kitchenGodCal = new CacheKitchenGodCal();
            this.ownChefs = ownChefs;
            this.ownRecipes = ownRecipes;
            this.ownEquips = ownEquips;
            this.reward=reward;
            tempCalCache = new TempCalCache(ownChefs.length, ownEquips.length, ownRecipes.length);
        }

        private void updateIdAndAddEffect(){

            for (int i = 0; i < ownChefs.length; i++) {
                Chef ownChef = ownChefs[i];
                ownChef.setIndex(i);
                buildEffect(ownChef);
            }
            for (int i = 0; i < ownEquips.length; i++) {
                Equip ownEquip = ownEquips[i];
                ownEquip.setIndex(i);
                buildEffect(ownEquip);
            }
        }

        public TempCalCache build() {
            updateIdAndAddEffect();
            initCache();
            return tempCalCache;
        }

        public void initCache() {
            int[][][] scoreCache = tempCalCache.scoreCache;
            int[][] socreCacheNoEquip=tempCalCache.socreCacheNoEquip;
            double[][][] qualityAddQ1 = tempCalCache.qualityAddQ;
            double[][][] qualityAddS1 = tempCalCache.qualityAddS;

            SkillEffect skillEffect ;
            double qualityAddQ, qualityAddS, price;
            int singleprice;

            double[] useall = globalAddtion.useall;
            //这个是厨师，菜谱的得分缓存
            for (int i = 0; i < ownChefs.length; i++) {
                Chef ownChef = ownChefs[i];
                    skillEffect = ownChef.getSkillEffect();
                    for (int i3 = 0; i3 < ownRecipes.length; i3++) {
                        CalRecipe ownRecipe = ownRecipes[i3];
                        int index=ownRecipe.index;
                        //品质加成
                        qualityAddQ = kitchenGodCal.QualityAddNoEquip(ownChef, skillEffect, ownRecipe);
                        if (qualityAddQ==-1){
                            socreCacheNoEquip[i][index] = 0;
                            continue;
                        }

                        //售价加成
                        qualityAddS = kitchenGodCal.skillAdd(skillEffect, ownRecipe);
                        price = ownRecipe.price;
                        //单份得分
                        singleprice = (int) Math.ceil(price * (1 + reward[ownRecipe.recipeId] + qualityAddQ + qualityAddS+useall[ownRecipe.rarity]));
                        socreCacheNoEquip[i][index] = singleprice*ownRecipe.count;
                    }
            }

            // 这个是厨师，厨具，菜谱的得分缓存
            for (int i = 0; i < ownChefs.length; i++) {
                Chef ownChef = ownChefs[i];
                for (int i2 = 0; i2 < ownEquips.length; i2++) {

                    Equip ownEquip = ownEquips[i2];
                    skillEffect = MergerSkillEffect(ownChef, ownEquip);
                    for (int i3 = 0; i3 < ownRecipes.length; i3++) {
                        CalRecipe ownRecipe = ownRecipes[i3];
                        int index=ownRecipe.index;
                        //品质加成
                        qualityAddQ = kitchenGodCal.QualityAdd(ownChef, skillEffect, ownRecipe);

                        if (qualityAddQ==-1){
                            qualityAddQ1[i][i2][index] = 0;
                            qualityAddS1[i][i2][index] = 0;
                            scoreCache[i][i2][index] = 0;
                            continue;
                        }
                        //售价加成
                        qualityAddS = kitchenGodCal.skillAdd(skillEffect, ownRecipe);
                        price = ownRecipe.price;
                        //单份得分
                        singleprice = (int) Math.ceil(price * (1 + reward[ownRecipe.recipeId] + qualityAddQ + qualityAddS+useall[ownRecipe.rarity]));
                        qualityAddQ1[i][i2][index] = qualityAddQ;
                        qualityAddS1[i][i2][index] = qualityAddS;
                        scoreCache[i][i2][index] = singleprice*ownRecipe.count;
                    }
                }
            }
        }

        //厨师的技能效果解析
        private SkillEffect buildEffect(Chef chef) {
            SkillEffect skillEffect = new SkillEffect();
            Skill skill = officialGameData.getSkill(chef.getSkill());
            List<Effect> effect = skill.getEffect();
            for (Effect effect1 : effect) {
                skillEffect.effect(effect1);
            }
            Integer ultimateId = chef.getUltimateSkill();
            if (ultimateId != null) {
                skill = officialGameData.getSkill(ultimateId);
                if (skill!=null){
                    effect = skill.getEffect();
                    for (Effect effect1 : effect) {
                        skillEffect.effect(effect1);
                    }
                }
            }
            chef.setSkillEffect(skillEffect);
            return skillEffect;
        }

        //设备的节能效果解析
        private SkillEffect buildEffect(Equip equip) {
            SkillEffect skillEffect = new SkillEffect();
            List<Integer> skills = equip.getSkill();
            for (Integer skill : skills) {
                Skill skill1 = officialGameData.getSkill(skill);
                List<Effect> effect = skill1.getEffect();
                for (Effect effect1 : effect) {
                    skillEffect.effect(effect1);
                }
            }
            equip.setSkillEffect(skillEffect);
            return skillEffect;
        }

        SkillEffect tempEffect = new SkillEffect();

        //合并厨师和厨具的技能效果
        private SkillEffect MergerSkillEffect(Chef chef, Equip equip) {
            tempEffect.reset();
            SkillEffect skillEffect = tempEffect;
            SkillEffect skillEffect1 = chef.getSkillEffect();
            SkillEffect skillEffect2 = equip.getSkillEffect();
            skillEffect.addEffect(skillEffect1);
            skillEffect.addEffect(skillEffect2);
            return skillEffect;
        }
    }


    public int getChefCount() {
        return chefCount;
    }

    public int getRecipeCount() {
        return recipeCount;
    }

    public void setRecipeCount(int recipeCount) {
        this.recipeCount = recipeCount;
    }

    public int getEquipCount() {
        return equipCount;
    }

    public void setEquipCount(int equipCount) {
        this.equipCount = equipCount;
    }

    public int[][][] getScoreCache() {
        return scoreCache;
    }

    public void setScoreCache(int[][][] scoreCache) {
        this.scoreCache = scoreCache;
    }

    public double[][][] getQualityAddS() {
        return qualityAddS;
    }

    public void setQualityAddS(double[][][] qualityAddS) {
        this.qualityAddS = qualityAddS;
    }

    public double[][][] getQualityAddQ() {
        return qualityAddQ;
    }

    public void setQualityAddQ(double[][][] qualityAddQ) {
        this.qualityAddQ = qualityAddQ;
    }
}

/**
 *  比如 int[70][80][500]
 * 一维： int[][][]
 *  8字节       对象头部
 *  4字节       数组长度描述
 *  4*70        一维的引用宽度
 *  padding 4
 * 一共 296 字节
 *
 * 二维 int[][]
 *  8*70字节       对象头部
 *  4*70字节       数组长度描述
 *  4*70*80         二维的引用宽度
 *  一共 23240 字节
 *
 * 三维 int[]
 *  8*70*80字节             对象头部
 *  4*70*80字节             数组长度描述
 *  4*500*70*80             存储内
 *  一共 11267200 字节
 *
 * 总占用字节数 11290736。
 *
 *
 * 比如 int[80][70][500]
 * 一维： int[][][]
 *  8字节       对象头部
 *  4字节       数组长度描述
 *  4*80        一维的引用宽度
 *  padding 4
 * 一共 336 字节
 *
 * 二维 int[][]
 *  8*80字节       对象头部
 *  4*80字节       数组长度描述
 *  4*70*80         二维的引用宽度
 *  一共 23360 字节
 *
 * 三维 int[]
 *  8*70*80字节             对象头部
 *  4*70*80字节             数组长度描述
 *  4*500*70*80             存储内容
 *  一共 11267200 字节
 *
 * 总占用字节数 11290896
 *
 *
 * 比如 int[500][80][70]
 * 一维： int[][][]
 *  8字节       对象头部
 *  4字节       数组长度描述
 *  4*500        一维的引用宽度
 *  padding 4
 * 一共 2016 字节
 *
 * 二维 int[][]
 *  8*500字节       对象头部
 *  4*500字节       数组长度描述
 *  4*500*80         二维的引用宽度
 *  一共 166000 字节
 *
 * 三维 int[]
 *  8*500*80字节             对象头部
 *  4*500*80字节             数组长度描述
 *  4*500*80*70             存储内容
 *  一共 11680000 字节
 *
 * 总占用字节数 11848016
 * 。
 *
 */
